import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:deepseek_desktop/entity/ChatMessage.dart';
import 'package:deepseek_desktop/gpt/plugins/GPTPluginInterface.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:http/io_client.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:http/http.dart' as http;
import 'package:crypto/crypto.dart';

// import 'package:pdfx/pdfx.dart'; // PDF解析适配pdfx 2.9.2
import 'package:docx_to_text/docx_to_text.dart';
import 'package:image/image.dart';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import 'package:file_picker/file_picker.dart';

class Util {
  static Future<String> extractFileContent(PlatformFile file) async {
    try {
      final bytes = file.bytes;
      if (bytes == null) return '';

      // 根据文件类型调用不同解析方法
      switch (file.extension) {
        // case 'pdf':
        //   return await _parsePdf(bytes);
        case 'docx':
          return await _parseDocx(bytes);
        case 'txt':
          return String.fromCharCodes(bytes);
        case 'jpg':
        case 'jpeg':
        case 'png':
          return _parseImageWithOCR(bytes);
        default:
          return '不支持的文件类型：${file.extension}';
      }
    } catch (e) {
      return '文件解析失败：${e.toString()}';
    }
  }

  // static Future<String> _parsePdf(Uint8List bytes) async {
  //   final pdf = await PdfDocument.openData(bytes);
  //   final StringBuffer content = StringBuffer();

  //   try {
  //     for (int i = 1; i <= pdf.pagesCount; i++) {
  //       final page = await pdf.getPage(i);
  //       try {
  //         // 渲染页面为图像
  //         final image = await page.render(
  //           width: page.width.toDouble(), // 转换为整型
  //           height: page.height.toDouble(), // 转换为整型
  //           backgroundColor: '#FFFFFF', // 直接使用颜色字符串
  //         );

  //         if (image != null) {
  //           // 将渲染结果传递给OCR方法
  //           final text = await _parseImageWithOCR(image.bytes);
  //           if (text.isNotEmpty) {
  //             content.writeln(text);
  //           } else {
  //             content.writeln('第$i页没有提取到文本');
  //           }
  //         } else {
  //           content.writeln('第$i页渲染失败');
  //         }
  //       } catch (e) {
  //         content.writeln('第$i页解析失败: ${e.toString()}');
  //       } finally {
  //         await page.close();
  //       }
  //     }
  //   } finally {
  //     await pdf.close();
  //   }
  //   return content.toString();
  // }

  static Future<String> _parseDocx(Uint8List bytes) async {
    try {
      // 使用 docx_to_text 直接解析字节流
      final text = docxToText(bytes);

      // 清理多余空白字符
      final cleanedText = text.replaceAll(RegExp(r'\s+'), ' ').trim();

      return cleanedText.isNotEmpty ? cleanedText : '文档内容为空';
    } catch (e) {
      return 'DOCX解析异常: ${e.toString()}';
    }
  }

  static Future<String> _parseImageWithOCR(Uint8List bytes) async {
    try {
      final Size imageSize = _getImageSize(bytes);
      final inputImage = InputImage.fromBytes(
        bytes: bytes,
        metadata: InputImageMetadata(
            size: _getImageSize(bytes), // 建议改为实际图片尺寸
            rotation: InputImageRotation.rotation0deg,
            format: InputImageFormat.bgra8888,
            bytesPerRow: imageSize.width.toInt() * 4),
      );
      final textRecognizer =
          TextRecognizer(script: TextRecognitionScript.latin); // 拉丁语系识别
      final recognizedText = await textRecognizer.processImage(inputImage);
      await textRecognizer.close();
      return recognizedText.text;
    } catch (e) {
      return "OCR处理失败：$e";
    }
  }

  // 添加私有方法获取实际图片尺寸
  static Size _getImageSize(Uint8List bytes) {
    final image = decodeImage(bytes);
    return image != null
        ? Size(image.width.toDouble(), image.height.toDouble())
        : Size(300, 300);
  }

  static String getMd5(String text) {
    return md5.convert(utf8.encode(text)).toString();
  }

  static Future<String> copyImageToAppDir() async {
    final directory = await getApplicationCacheDirectory();

    // 假设您的图片文件位于assets文件夹中
    ByteData data = await rootBundle.load('assets/chatgpt.png');
    List<int> bytes = data.buffer.asUint8List();
    File imageFile = File('${directory.path}/default-avatar.png');
    await imageFile.writeAsBytes(bytes);

    return imageFile.path;
  }

  static Future<String> saveImageFormUrl(String url) async {
    final directory = await getApplicationCacheDirectory();
    File imageFile = File('${directory.path}/cache/${getMd5(url)}');
    if (imageFile.existsSync() && imageFile.lengthSync() != 0) {
      return imageFile.path;
    } else {
      imageFile.createSync(recursive: true);
    }
    Uri uri = Uri.parse(url);
    var request = http.Request('GET', uri);
    // http.Client client = http.Client();
    HttpClient httpClient = new HttpClient();
    httpClient.badCertificateCallback =
        ((X509Certificate cert, String host, int port) => true);
    IOClient client = new IOClient(httpClient);
    return client.send(request).then((response) {
      return response.stream.toBytes().then((value) {
        return imageFile.writeAsBytes(value).then((value) {
          return imageFile.path;
        });
      });
    });
  }

  static Future<String> saveImageFormByteData(ByteData byteData) async {
    final directory = await getApplicationCacheDirectory();
    File imageFile = File(
        '${directory.path}/cache/${getMd5(DateTime.now().toString())}.png');
    await imageFile.writeAsBytes(byteData.buffer.asUint8List());
    return imageFile.path;
  }

  static Future<String> saveImageFormBytes(Uint8List imageBytes) async {
    final directory = await getApplicationCacheDirectory();
    File imageFile = File(
        '${directory.path}/cache/${getMd5(DateTime.now().toString())}.png');
    await imageFile.writeAsBytes(imageBytes);
    return imageFile.path;
  }

  static Future<String> pickAndSaveImage() async {
    final imagePicker = ImagePicker();
    final pickedFile = await imagePicker.pickImage(source: ImageSource.gallery);

    if (pickedFile != null) {
      final directory = await getApplicationCacheDirectory();
      final File imageFile =
          File('${directory.path}/select-images/${pickedFile.name}');
      if (imageFile.existsSync() == false) {
        imageFile.createSync(recursive: true);
      }
      await imageFile.writeAsBytes(await pickedFile.readAsBytes());
      return imageFile.path;
    }
    return '';
  }

  static void writeFile(String filename, String content) async {
    try {
      final directory = await getApplicationCacheDirectory();
      final File imageFile = File('${directory.path}/files/$filename');
      if (!imageFile.existsSync()) {
        await imageFile.create(recursive: true);
      }
      await imageFile.writeAsString(content, flush: true);
    } catch (e) {
      debugPrint('写入文件失败: $e');
    }
  }

  static Future<String> readFile(String filename) async {
    final directory = await getApplicationCacheDirectory();
    debugPrint('Reading file from: ${directory.path}/files/$filename');
    final File file = File('${directory.path}/files/$filename');
    if (!file.existsSync()) {
      return '';
    }
    return await file.readAsString();
  }

  static void postStream(String url, Map<String, String> header,
      Map<String, dynamic> body, Function callback) async {
    Uri uri = Uri.parse(url);
    var request = http.Request('POST', uri);

    request.body = jsonEncode(body);

    Map<String, String> headers = {"Content-type": "application/json"};
    headers.addAll(header);
    // http.Client client = http.Client();
    HttpClient httpClient = new HttpClient();
    httpClient.badCertificateCallback =
        ((X509Certificate cert, String host, int port) => true);
    IOClient client = new IOClient(httpClient);
    client.head(uri, headers: headers);
    client.send(request).then((response) {
      response.stream.listen((List<int> data) {
        callback(data);
        print(data);
      });
    });
  }

  static Future<String> post(
      String url, Map<String, String> header, Map<String, dynamic> body) async {
    Uri uri = Uri.parse(url);
    var request = http.Request('POST', uri);

    request.body = jsonEncode(body);

    Map<String, String> headers = {
      // "Content-type": "application/json"
    };
    headers.addAll(header);
    request.headers.addAll(headers);
    // http.Client client = http.Client();
    // 不校验证书
    HttpClient httpClient = new HttpClient();
    httpClient.badCertificateCallback =
        ((X509Certificate cert, String host, int port) => true);
    IOClient client = new IOClient(httpClient);
    return client.send(request).then((response) {
      return response.stream.bytesToString();
    });
  }

  static IOClient askGPT(
      String basicUrl,
      String model,
      double temperature,
      String accessKey,
      List<GPTPluginInterface> plugin,
      List<ChatMessage> message,
      GPTCallbackFunction callback,
      Function err,
      {bufferContent = '',
      required RxBool isSending,
      required RxBool isGrayColor}) {
    debugPrint(jsonEncode(message));
    final messages = [];
    for (var m in message) {
      if (m.toolsCallId.isNotEmpty) {
        messages.add({
          "role": m.role,
          "content": m.content,
          "tool_call_id": m.toolsCallId
        });
      } else {
        if (m.toolCalls != null) {
          messages.add({
            "role": m.role,
            "content": m.content,
            "tool_calls": [m.toolCalls?.toMap()]
          });
        } else {
          messages.add({
            "role": m.role,
            "content": m.content,
          });
        }
      }
    }
    List<dynamic> tools = [];
    final requestBody = {
      "model": model,
      "messages": messages,
      "temperature": temperature,
      "stream": true,
    };

    // 添加插件
    for (var p in plugin) {
      for (var method in p.methods) {
        tools.add({
          "type": "function",
          "function": method.toMap(),
        });
      }
    }
    if (tools.isNotEmpty) {
      requestBody["tools"] = tools;
    }

    // 开始请求
    // var client = http.Client();
    // 不校验证书
    HttpClient httpClient = new HttpClient();
    httpClient.badCertificateCallback =
        ((X509Certificate cert, String host, int port) => true);
    IOClient ioClient = new IOClient(httpClient);

    if (basicUrl.isEmpty) {
      callback("服务器地址为空，请跳转至 API 设置页面进行设置", true);
      return ioClient;
    }
    if (accessKey.isEmpty) {
      callback("API Key 为空，请跳转至 API 设置页面进行设置", true);
      return ioClient;
    }
    if (model.isEmpty) {
      callback("模型为空，请跳转至 API 设置页面进行设置", true);
      return ioClient;
    }
    basicUrl = basicUrl.endsWith('/') ? basicUrl : '$basicUrl/';
    var request =
        http.Request("POST", Uri.parse('${basicUrl}v1/chat/completions'));
    debugPrint("Request URL: ${request.url}");
    request.headers["Authorization"] = "Bearer $accessKey";
    request.headers["Content-Type"] = "application/json; charset=UTF-8";
    request.body = jsonEncode(requestBody);

    ioClient.send(request).then((response) {
      String showContent = bufferContent;
      String callFunctionId = "";
      String callFunction = "";
      String callFunctionParams = "";

      final stream = response.stream.transform(utf8.decoder);
      // 监听接收的数据
      StreamSubscription? subscription;
      subscription = stream.listen(
        (data) {
          // 检查是否应该中断
          if (!isSending.value) {
            subscription?.cancel();
            callback(showContent, true);
            return;
          }
          if (response.statusCode != 200) {
            String finalData = data;
            if (!finalData.endsWith("```")) {
              if (finalData.contains('<html>')) {
                finalData = "\n```html\n$finalData\n```";
              } else {
                finalData = "\n```json\n$finalData\n```";
              }
            }
            showContent += "Request failed: ${response.statusCode}\n$finalData";
            callback(showContent, false);
            return;
          }
          final dataLines =
              data.split("\n").where((element) => element.isNotEmpty).toList();
          for (String line in dataLines) {
            // 丢弃前6个字符：“data: ”
            if (!line.startsWith("data: ")) continue;
            final data = line.substring(6);

            if (data == "[DONE]") break; // 表示接收已完成

            // 解析数据
            Map<String, dynamic> responseData = json.decode(data);
            List<dynamic> choices = responseData["choices"];
            Map<String, dynamic> choice = choices[0];
            Map<String, dynamic> delta = choice["delta"];
            String content = delta["content"] ?? "";
            if (delta["tool_calls"] != null && delta["tool_calls"].isNotEmpty) {
              // 插件调用
              var tool = delta["tool_calls"][0];
              if (tool["function"] != null) {
                if (tool["function"]["name"] != null) {
                  callFunction = tool["function"]["name"];
                  callFunctionId = tool["id"];
                  showContent += "\n\n```call-function\n$callFunction";
                }
                callFunctionParams += tool["function"]["arguments"];
              }
            }

            // 拼接并展示数据
            showContent += content;
            callback(showContent, false);
            if (choice["finish_reason"] != null) break; // 表示接收已完成
          }
        },
        onDone: () {
          if (callFunction.isNotEmpty) {
            // 执行插件方法
            for (var p in plugin) {
              for (var method in p.methods) {
                if (method.name == callFunction) {
                  message.add(ChatMessage(
                      content: "",
                      role: 'assistant',
                      time: '',
                      toolCalls: ToolCalls(
                          id: callFunctionId,
                          type: 'function',
                          function: ToolCallsFunction(
                              name: method.name,
                              arguments: callFunctionParams))));
                  Map<String, dynamic> functionParam =
                      jsonDecode(callFunctionParams);
                  p
                      .execute(method.name, functionParam, basicUrl, accessKey)
                      .then((value) {
                    message.add(ChatMessage(
                        content: value,
                        role: 'tool',
                        time: '',
                        toolsCallId: callFunctionId));
                    showContent += '!end\n```\n';
                    askGPT(basicUrl, model, temperature, accessKey, plugin,
                        message, callback, err,
                        bufferContent: showContent,
                        isSending: isSending,
                        isGrayColor: isGrayColor);
                  });
                  return;
                }
              }
            }
          } else {
            callback(showContent, true);
          }
        },
        onError: (error) {
          err(error);
          isSending.value = false;
          isGrayColor.value = true;
        },
      );
    }).catchError((error) {
      err(error);
      isSending.value = false;
      isGrayColor.value = true;
    });
    return ioClient;
  }
}

typedef GPTCallbackFunction = void Function(String result, bool finish);
